package org.ehe.finance.receivable.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ehe.common.core.exception.ServiceException;
import org.ehe.common.core.utils.MapstructUtils;
import org.ehe.common.core.utils.StringUtils;
import org.ehe.common.mybatis.core.page.PageQuery;
import org.ehe.common.mybatis.core.page.TableDataInfo;
import org.ehe.common.satoken.utils.LoginHelper;
import org.ehe.finance.receivable.domain.ErpFinanceReceivable;
import org.ehe.finance.receivable.domain.bo.ErpFinanceReceivableBo;
import org.ehe.finance.receivable.domain.vo.ErpFinanceReceivableVo;
import org.ehe.finance.receivable.mapper.ErpFinanceReceivableMapper;
import org.ehe.finance.receivable.service.ErpFinanceReceivableService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * 应收账款服务实现
 * @author :zhangnn
 * @className :ErpFinanceReceivableServiceImpl
 * @description: 应收账款管理服务实现
 * @date 2025-08-28 10:00:00
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ErpFinanceReceivableServiceImpl extends ServiceImpl<ErpFinanceReceivableMapper, ErpFinanceReceivable>
    implements ErpFinanceReceivableService {

    /**
     * 分页查询应收账款列表
     */
    @Override
    public TableDataInfo<ErpFinanceReceivableVo> queryPageList(ErpFinanceReceivableBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<ErpFinanceReceivable> lqw = buildQueryWrapper(bo);
        Page<ErpFinanceReceivableVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);

        // 计算逾期天数
        result.getRecords().forEach(this::calculateOverdueDays);

        return TableDataInfo.build(result);
    }

    /**
     * 查询应收账款列表
     */
    @Override
    public List<ErpFinanceReceivableVo> queryList(ErpFinanceReceivableBo bo) {
        LambdaQueryWrapper<ErpFinanceReceivable> lqw = buildQueryWrapper(bo);
        List<ErpFinanceReceivableVo> list = baseMapper.selectVoList(lqw);

        // 计算逾期天数
        list.forEach(this::calculateOverdueDays);

        return list;
    }

    /**
     * 构建查询条件
     */
    private LambdaQueryWrapper<ErpFinanceReceivable> buildQueryWrapper(ErpFinanceReceivableBo bo) {
        LambdaQueryWrapper<ErpFinanceReceivable> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getBillNo()), ErpFinanceReceivable::getBillNo, bo.getBillNo());
        lqw.eq(bo.getCustomerId() != null, ErpFinanceReceivable::getCustomerId, bo.getCustomerId());
        lqw.eq(bo.getBillType() != null, ErpFinanceReceivable::getBillType, bo.getBillType());
        lqw.like(StringUtils.isNotBlank(bo.getSourceBillNo()), ErpFinanceReceivable::getSourceBillNo, bo.getSourceBillNo());
        lqw.eq(bo.getCurrencyId() != null, ErpFinanceReceivable::getCurrencyId, bo.getCurrencyId());
        lqw.eq(bo.getDepartmentId() != null, ErpFinanceReceivable::getDepartmentId, bo.getDepartmentId());
        lqw.eq(bo.getSalesmanId() != null, ErpFinanceReceivable::getSalesmanId, bo.getSalesmanId());
        lqw.eq(bo.getSettlementStatus() != null, ErpFinanceReceivable::getSettlementStatus, bo.getSettlementStatus());
        lqw.in(bo.getSettlementStatusList() != null && bo.getSettlementStatusList().length > 0,
            ErpFinanceReceivable::getSettlementStatus, List.of(bo.getSettlementStatusList()));

        // 日期范围查询
        lqw.ge(bo.getBillDateStart() != null, ErpFinanceReceivable::getBillDate, bo.getBillDateStart());
        lqw.le(bo.getBillDateEnd() != null, ErpFinanceReceivable::getBillDate, bo.getBillDateEnd());
        lqw.ge(bo.getDueDateStart() != null, ErpFinanceReceivable::getDueDate, bo.getDueDateStart());
        lqw.le(bo.getDueDateEnd() != null, ErpFinanceReceivable::getDueDate, bo.getDueDateEnd());

        // 逾期查询
        if (bo.getIsOverdue() != null && bo.getIsOverdue()) {
            lqw.lt(ErpFinanceReceivable::getDueDate, LocalDate.now());
            lqw.in(ErpFinanceReceivable::getSettlementStatus, List.of(1, 2)); // 未结算或部分结算
        }

        lqw.orderByDesc(ErpFinanceReceivable::getCreateTime);
        return lqw;
    }

    /**
     * 计算逾期天数
     */
    private void calculateOverdueDays(ErpFinanceReceivableVo vo) {
        if (vo.getDueDate() != null && (vo.getSettlementStatus() == 1 || vo.getSettlementStatus() == 2)) {
            LocalDate now = LocalDate.now();
            if (vo.getDueDate().isBefore(now)) {
                vo.setOverdueDays((int) ChronoUnit.DAYS.between(vo.getDueDate(), now));
                vo.setIsOverdue(true);
            } else {
                vo.setOverdueDays(0);
                vo.setIsOverdue(false);
            }
        } else {
            vo.setOverdueDays(0);
            vo.setIsOverdue(false);
        }
    }

    /**
     * 查询应收账款详情
     */
    @Override
    public ErpFinanceReceivableVo queryById(Long id) {
        ErpFinanceReceivableVo vo = baseMapper.selectVoById(id);
        if (vo != null) {
            calculateOverdueDays(vo);
        }
        return vo;
    }

    /**
     * 新增应收账款
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(ErpFinanceReceivableBo bo) {
        ErpFinanceReceivable add = MapstructUtils.convert(bo, ErpFinanceReceivable.class);
        validEntityBeforeSave(add);

        // 设置租户ID
        add.setTenantId(LoginHelper.getTenantId());

        // 计算未收款金额
        if (add.getPaidAmount() == null) {
            add.setPaidAmount(BigDecimal.ZERO);
        }
        add.setUnpaidAmount(add.getTotalAmount().subtract(add.getPaidAmount()));

        // 设置默认结算状态
        if (add.getSettlementStatus() == null) {
            add.setSettlementStatus(1); // 未结算
        }

        // 设置默认汇率
        if (add.getExchangeRate() == null) {
            add.setExchangeRate(new BigDecimal("1.0000"));
        }

        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改应收账款
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(ErpFinanceReceivableBo bo) {
        ErpFinanceReceivable update = MapstructUtils.convert(bo, ErpFinanceReceivable.class);
        validEntityBeforeSave(update);

        // 重新计算未收款金额
        if (update.getPaidAmount() != null && update.getTotalAmount() != null) {
            update.setUnpaidAmount(update.getTotalAmount().subtract(update.getPaidAmount()));
        }

        // 根据收款情况更新结算状态
        updateSettlementStatus(update);

        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(ErpFinanceReceivable entity) {
        // 校验单据编号唯一性
        if (StringUtils.isNotBlank(entity.getBillNo())) {
            boolean isUnique = checkBillNoUnique(entity.getBillNo(), entity.getId());
            if (!isUnique) {
                throw new ServiceException("单据编号已存在");
            }
        }

        // 校验金额
        if (entity.getTotalAmount() != null && entity.getTotalAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("应收总金额必须大于0");
        }

        if (entity.getPaidAmount() != null && entity.getPaidAmount().compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("已收款金额不能小于0");
        }

        if (entity.getTotalAmount() != null && entity.getPaidAmount() != null
            && entity.getPaidAmount().compareTo(entity.getTotalAmount()) > 0) {
            throw new ServiceException("已收款金额不能大于应收总金额");
        }
    }

    /**
     * 更新结算状态
     */
    private void updateSettlementStatus(ErpFinanceReceivable entity) {
        if (entity.getTotalAmount() != null && entity.getPaidAmount() != null) {
            if (entity.getPaidAmount().compareTo(BigDecimal.ZERO) == 0) {
                entity.setSettlementStatus(1); // 未结算
            } else if (entity.getPaidAmount().compareTo(entity.getTotalAmount()) == 0) {
                entity.setSettlementStatus(3); // 已结算
            } else {
                entity.setSettlementStatus(2); // 部分结算
            }
        }
    }

    /**
     * 批量删除应收账款
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // 校验是否存在已收款记录
            List<ErpFinanceReceivable> list = baseMapper.selectBatchIds(ids);
            for (ErpFinanceReceivable receivable : list) {
                if (receivable.getPaidAmount() != null && receivable.getPaidAmount().compareTo(BigDecimal.ZERO) > 0) {
                    throw new ServiceException("单据【" + receivable.getBillNo() + "】已有收款记录，不能删除");
                }
            }
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 检查单据编号唯一性
     */
    @Override
    public boolean checkBillNoUnique(String billNo, Long id) {
        LambdaQueryWrapper<ErpFinanceReceivable> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ErpFinanceReceivable::getBillNo, billNo);
        if (id != null) {
            lqw.ne(ErpFinanceReceivable::getId, id);
        }
        return baseMapper.selectCount(lqw) == 0;
    }

    /**
     * 应收账款收款
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updatePayment(Long id, BigDecimal paymentAmount) {
        ErpFinanceReceivable receivable = baseMapper.selectById(id);
        if (receivable == null) {
            throw new ServiceException("应收账款不存在");
        }

        if (paymentAmount == null || paymentAmount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("收款金额必须大于0");
        }

        BigDecimal newPaidAmount = receivable.getPaidAmount().add(paymentAmount);
        if (newPaidAmount.compareTo(receivable.getTotalAmount()) > 0) {
            throw new ServiceException("收款金额超过未收款金额");
        }

        // 更新收款信息
        LambdaUpdateWrapper<ErpFinanceReceivable> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ErpFinanceReceivable::getId, id)
            .set(ErpFinanceReceivable::getPaidAmount, newPaidAmount)
            .set(ErpFinanceReceivable::getUnpaidAmount, receivable.getTotalAmount().subtract(newPaidAmount));

        // 更新结算状态
        if (newPaidAmount.compareTo(receivable.getTotalAmount()) == 0) {
            updateWrapper.set(ErpFinanceReceivable::getSettlementStatus, 3); // 已结算
        } else {
            updateWrapper.set(ErpFinanceReceivable::getSettlementStatus, 2); // 部分结算
        }

        return baseMapper.update(null, updateWrapper) > 0;
    }

    /**
     * 获取客户应收账款汇总
     */
    @Override
    public ErpFinanceReceivableVo getCustomerReceivableSummary(Long customerId) {
        return baseMapper.selectCustomerReceivableSummary(customerId);
    }

    /**
     * 批量收款
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchPayment(List<ErpFinanceReceivableBo> boList) {
        for (ErpFinanceReceivableBo bo : boList) {
            if (bo.getId() == null || bo.getPaidAmount() == null) {
                throw new ServiceException("收款信息不完整");
            }
            updatePayment(bo.getId(), bo.getPaidAmount());
        }
        return true;
    }
}
